package com.idanchuang.component.mybatis.plus.plugins;

import com.idanchuang.component.logback.helper.LogContext;
import com.idanchuang.component.logback.metric.MetricLogger;
import com.idanchuang.component.mybatis.plus.config.MybatisConfigBean;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * SQL执行时间
 *
 * @author yjy
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class })})
public class ProcessTimeInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(ProcessTimeInterceptor.class);

    private final MybatisConfigBean mybatisConfigBean;

    public ProcessTimeInterceptor(MybatisConfigBean mybatisConfigBean) {
        this.mybatisConfigBean = mybatisConfigBean;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        boolean success = true;
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } catch (Exception e) {
            success = false;
            throw e;
        } finally {
            doLogger(invocation, start, success);
        }
    }

    private void doLogger(Invocation invocation, long startTime, boolean success) {
        String sql = null;
        try {
            long rt = System.currentTimeMillis() - startTime;
            if (rt > mybatisConfigBean.getSlowTime()) {
                sql = getSql(invocation);
                LogContext.setKeyword("slowSql");
                logger.warn("Slow Sql > cost: {}ms, sql: {}", rt, sql);
            }
            // if log
            if (mybatisConfigBean.isLogRt()) {
                if (sql == null) {
                    sql = getSql(invocation);
                }
                Map<String, Object> tags = new HashMap<>();
                tags.put("type", sql.split(" ")[0].toLowerCase());
                tags.put("sql", sql);
                tags.put("success", success ? "true" : "false");
                MetricLogger.log(mybatisConfigBean.getRtMetricName(), rt, tags);
            }
        } catch (Exception e) {
            logger.error("ProcessTimeInterceptor error", e);
        }
    }

    /**
     * 获取sql
     * @param invocation ..
     * @return sql
     */
    private String getSql(Invocation invocation) {
        Object target = invocation.getTarget();
        StatementHandler statementHandler = (StatementHandler) target;
        BoundSql boundSql = statementHandler.getBoundSql();
        return boundSql.getSql();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
